package org.qydjk.common.shiro;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.qydjk.common.redis.RedisClient;
import org.qydjk.common.util.SerializeUtil;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 输错5次密码锁定半小时，ehcache.xml配置
 */
public class RetryLimitCredentialsMatcher extends HashedCredentialsMatcher implements InitializingBean {
	private final static Logger logger = LogManager.getLogger(RetryLimitCredentialsMatcher.class);
	private final static String DEFAULT_CHACHE_NAME = "retryLimitCache:";
	
	private String keyPrefix;
	private PasswordHash passwordHash;

	private RedisClient redisClient;
	
	public RetryLimitCredentialsMatcher() {
		this.keyPrefix = DEFAULT_CHACHE_NAME;
	}
	
	public void setRetryLimitCacheName(String retryLimitCacheName) {
		this.keyPrefix = retryLimitCacheName;
	}

	public void setRedisClient(RedisClient redisClient) {
		this.redisClient = redisClient;
	}

	public void setPasswordHash(PasswordHash passwordHash) {
		this.passwordHash = passwordHash;
	}

	@Override
	public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
		String username = (String) authcToken.getPrincipal();
		AtomicInteger retryCount = null;
		byte[] val = redisClient.get(buildKey(username));
		if (val != null && val.length > 0) {
			retryCount = SerializeUtil.deserialize(val, AtomicInteger.class);
		}else{
			retryCount = new AtomicInteger(0);
			redisClient.set(buildKey(username),SerializeUtil.serialize(retryCount));
		}

		if(retryCount.incrementAndGet() > 10) {
			logger.warn("username: " + username + " tried to login more than 5 times in period");
			throw new ExcessiveAttemptsException("用户名: " + username + " 密码连续输入错误超过5次，锁定半小时！");
		}

		boolean matches = super.doCredentialsMatch(authcToken, info);
		if(matches) {
			redisClient.del(buildKey(username));
		}else{
			redisClient.set(buildKey(username),SerializeUtil.serialize(retryCount));
		}
		return matches;
	}

	private byte[] buildKey(String key){
		return SerializeUtil.serialize(keyPrefix + key);
	}
	
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(passwordHash, "you must set passwordHash!");
		super.setHashAlgorithmName(passwordHash.getAlgorithmName());
		super.setHashIterations(passwordHash.getHashIterations());
	}

	public void setKeyPrefix(String keyPrefix) {
		this.keyPrefix = keyPrefix;
	}
}
